
package com.niudong.esdemo.pscsoft;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHost;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.ShardSearchFailure;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.Avg;
import org.elasticsearch.search.aggregations.metrics.Sum;
import org.elasticsearch.search.aggregations.metrics.SumAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.profile.ProfileShardResult;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.SuggestionBuilder;
import org.elasticsearch.search.suggest.term.TermSuggestion;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class QueryByWhere2 {
    public static void main(String[] args) {
        initEs();

        SearchRequest player = buildSearchRequest("player");
        executeSearchRequest(player);
        closeEs();
    }

    private static RestHighLevelClient restClient;
    private static Log log = LogFactory.getLog(QueryByWhere2.class);
    // 初始化连接


    public static void initEs() {
        restClient = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"),
                new HttpHost("localhost", 9201, "http")));

        log.info("ElasticSearch init in service.");
    }

    // 关闭连接
    public static void closeEs() {
        try {
            restClient.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void where(String index,String one, String two, WhereCondition condition) {
        switch (condition) {
            case eq:
                break;
            case neq:
                break;
            case gt:
                break;
            case lt:
                break;
            case gte:
                break;
            case lte:
                break;
            case like:
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + condition);
        }
    }


    public static SearchRequest buildSearchRequest(String indices) {
        SearchRequest searchRequest = new SearchRequest();

        // 大多数搜索参数都添加到SearchSourceBuilder中。它为进入搜索请求主体的所有内容提供setter。
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 向SearchSourceBuilder添加“全部匹配”查询。
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());



        /*
         * 可选参数配置
         */

        // 在索引上限制请求
        searchRequest = new SearchRequest(indices);

        // 设置路由参数
        searchRequest.routing("routing");

        // 设置IndiceOptions控制方法： 如何解析不可用索引以及如何扩展通配符表达式
        searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());

        // 使用首选参数，例如执行搜索以首选本地分片。默认值是在分片之间随机化。
        searchRequest.preference("_local");


        /*
         * 聚合请求的使用
         */
        TermsAggregationBuilder aggregation =
                AggregationBuilders.terms("team_group_sum").field("team");
        aggregation.subAggregation(AggregationBuilders.sum("sum_salary").field("salary"));
        searchSourceBuilder.aggregation(aggregation);


        TermsAggregationBuilder aggregation2 =
                AggregationBuilders.terms("team_group_avg").field("team");
        aggregation2.subAggregation(AggregationBuilders.avg("avg_salary").field("salary"));
        searchSourceBuilder.aggregation(aggregation2);

        // 将SearchSourceBuilder添加到seachRequest。
        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }


    // 参数化构建SearchRequest
    public SearchRequest buildSearchRequest(String filed, String text) {
        SearchRequest searchRequest = new SearchRequest();

        // 大多数搜索参数都添加到SearchSourceBuilder中。它为进入搜索请求主体的所有内容提供setter。
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 向SearchSourceBuilder添加“全部匹配”查询。
        //searchSourceBuilder.query(QueryBuilders.matchAllQuery());

        // 将SearchSourceBuilder添加到seachRequest。
        searchRequest.source(searchSourceBuilder);

        /*
         * 使用SearchSourceBuilder
         */

        // 设置查询条件
        searchSourceBuilder.query(QueryBuilders.termQuery(filed, text));
        // 设置搜索结果索引的起始地址，默认为0。
        searchSourceBuilder.from(0);
        // 设置要返回的搜索命中数的大小。默认值为10。
        searchSourceBuilder.size(5);
        // 设置一个可选的超时，控制允许搜索的时间。
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        // 将SearchSourceBuilder添加到SearchRequest中：
        searchRequest.source(searchSourceBuilder);

        /*
         * 配置请求高亮显示
         */

        HighlightBuilder highlightBuilder = new HighlightBuilder();

        // 为title字段创建字段高亮
        HighlightBuilder.Field highlightTitle = new HighlightBuilder.Field(filed);

        // 设置字段高亮类型
        highlightTitle.highlighterType("unified");

        // 将highlightTitle添加到highlightBuilder
        highlightBuilder.field(highlightTitle);

        searchSourceBuilder.highlighter(highlightBuilder);

        /*
         * Suggestions 建议请求的使用
         */

        // TermSuggestionBuilder 中为content字段添加货币的Suggestions
        SuggestionBuilder termSuggestionBuilder = SuggestBuilders.termSuggestion(filed).text(text);
        SuggestBuilder suggestBuilder = new SuggestBuilder();

        // 添加Suggestions建议生成器 并命名
        suggestBuilder.addSuggestion("suggest_user", termSuggestionBuilder);

        // 添加suggestBuilder到searchSourceBuilder
        searchSourceBuilder.suggest(suggestBuilder);

        return searchRequest;
    }

    // 同步方式执行SearchRequest
    public static void executeSearchRequest(SearchRequest searchRequest) {

        // 运行
        try {
            SearchResponse searchResponse = restClient.search(searchRequest, RequestOptions.DEFAULT);
            log.info(searchResponse.toString());
            // 解析SearchResponse
            processSearchResponse(searchResponse);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭ES的连接
            closeEs();
        }
    }


    // 解析SearchResponse
    private static void processSearchResponse(SearchResponse searchResponse) {
        if (searchResponse == null) {
            return;
        }

        // 获取HTTP状态代码
        RestStatus status = searchResponse.status();
        // 获取请求执行时间
        TimeValue took = searchResponse.getTook();
        // 获取请求是否提前终止
        Boolean terminatedEarly = searchResponse.isTerminatedEarly();
        // 获取请求是否超时
        boolean timedOut = searchResponse.isTimedOut();
        log.info("status is " + status + ";took is " + took + ";terminatedEarly is " + terminatedEarly
                + ";timedOut is " + timedOut);

        // 查看搜索影响的分片总数
        int totalShards = searchResponse.getTotalShards();
        // 执行搜索成功的分片的统计信息
        int successfulShards = searchResponse.getSuccessfulShards();
        // 执行搜索失败的分片的统计信息
        int failedShards = searchResponse.getFailedShards();
        log.info("totalShards is " + totalShards + ";successfulShards is " + successfulShards
                + ";failedShards is " + failedShards);
        System.out.println("totalShards is " + totalShards + ";successfulShards is " + successfulShards
                + ";failedShards is " + failedShards);

        for (ShardSearchFailure failure : searchResponse.getShardFailures()) {
            log.info("fail is " + failure.toString());
            System.out.println("fail is " + failure.toString());
        }

        // 获取响应中包含的搜索结果
        SearchHits hits = searchResponse.getHits();
        // SearchHits提供有关所有结果的全局信息，如点击总数或最高分数：
        TotalHits totalHits = hits.getTotalHits();
        // 点击总数
        long numHits = totalHits.value;
        // 最高分数
        float maxScore = hits.getMaxScore();
        log.info("numHits is " + numHits + ";maxScore is " + maxScore);
        System.out.println("numHits is " + numHits + ";maxScore is " + maxScore);
        // 嵌套在searchhits中的是可以迭代的单个搜索结果
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            // SearchHit提供对基本信息的访问，如索引、文档ID和每次搜索的得分：
            String index = hit.getIndex();
            String id = hit.getId();
            float score = hit.getScore();

            System.out.println("docId is " + id + ";docIndex is " + index + ";docScore is " + score);
            log.info("docId is " + id + ";docIndex is " + index + ";docScore is " + score);
            // 以JSON字符串形式返回文档源
            String sourceAsString = hit.getSourceAsString();
            // 以键/值对的映射形式返回文档源
            //name=郭德纲, team=cav, position=sf, salary=3000, age=33
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String team = (String) sourceAsMap.get("team");
            String position = (String) sourceAsMap.get("position");
            Integer salary = (Integer) sourceAsMap.get("salary");
            Integer age = (Integer) sourceAsMap.get("age");
            List<Object> users = (List<Object>) sourceAsMap.get("user");
            System.out.println(name + team + position + salary + age);
            Map<String, Object> innerObject = (Map<String, Object>) sourceAsMap.get("innerObject");
            log.info("sourceAsString is " + sourceAsString + ";sourceAsMap size is " + sourceAsMap.size());
            System.out.println("sourceAsString is " + sourceAsString + ";sourceAsMap size is " + sourceAsMap.size());
/*            // 高亮显示
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField highlight = highlightFields.get("content");
            // 获取包含高亮显示的字段内容的一个或多个片段
            Text[] fragments = highlight.fragments();
            String fragmentString = fragments[0].string();
            log.info("fragmentString is " + fragmentString);*/
        }

        // 聚合搜索
        Aggregations aggregations = searchResponse.getAggregations();
        if (aggregations == null) {
            return;
        }
        System.out.println("=======================");
        for (Aggregation aggregation : aggregations) {
            System.out.println("name:"+aggregation.getName()+"\ttype:"+aggregation.getType());
            switch (aggregation.getName()){
                case "team_group_sum":
                    Terms byCompanyAggregation = aggregations.get("team_group_sum");
                    List<? extends Terms.Bucket> buckets = byCompanyAggregation.getBuckets();
                    for (Terms.Bucket bucket : buckets) {
                        String key = bucket.getKey().toString();
                        System.out.println(bucket.getKey());
                        Terms.Bucket elasticBucket = byCompanyAggregation.getBucketByKey(key);
                        Sum sumAge = elasticBucket.getAggregations().get("sum_salary");
                        double sum = sumAge.getValue();
                        log.info("sum is " + sum);
                        System.out.println("sum is " + sum);
                    }
                    break;
                case "team_group_avg":
                    Terms team_group_avg = aggregations.get("team_group_avg");
                    List<? extends Terms.Bucket> buckets2 = team_group_avg.getBuckets();
                    for (Terms.Bucket bucket : buckets2) {
                        String key = bucket.getKey().toString();
                        System.out.println(bucket.getKey());
                        Terms.Bucket elasticBucket = team_group_avg.getBucketByKey(key);
                        Avg avgSalary = elasticBucket.getAggregations().get("avg_salary");
                        double avg = avgSalary.getValue();
                        log.info("avg is " + avg);
                        System.out.println("avg is " + avg);
                    }
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + aggregation.getName());
            }
        }


        // Suggest搜索
        Suggest suggest = searchResponse.getSuggest();
        if (suggest == null) {
            return;
        }
        // 按content搜索Suggest
        TermSuggestion termSuggestion = suggest.getSuggestion("content");
        for (TermSuggestion.Entry entry : termSuggestion.getEntries()) {
            for (TermSuggestion.Entry.Option option : entry) {
                String suggestText = option.getText().string();
                log.info("suggestText is " + suggestText);
            }
        }

        // 搜索时分析结果
        Map<String, ProfileShardResult> profilingResults = searchResponse.getProfileResults();
        if (profilingResults == null) {
            return;
        }
        for (Map.Entry<String, ProfileShardResult> profilingResult : profilingResults.entrySet()) {
            String key = profilingResult.getKey();
            ProfileShardResult profileShardResult = profilingResult.getValue();
            log.info("key is " + key + ";profileShardResult is " + profileShardResult.toString());
        }
    }

}
